Tumor evolution project

Data used

In this notebook, we are using the tmb_genomic.tsv file generated from the 01-preprocess-data.Rmd script.

Set up

suppressPackageStartupMessages({
  library(tidyverse)
})

Directories and File Inputs/Outputs

# Detect the ".git" folder. This will be in the project root directory.
# Use this as the root directory to ensure proper sourcing of functions
# no matter where this is called from.
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
scratch_dir <- file.path(root_dir, "scratch")
analysis_dir <- file.path(root_dir, "analyses", "tmb-vaf-longitudinal") 
input_dir <- file.path(analysis_dir, "input")

# Input files
tmb_genomic_file <- file.path(scratch_dir, "tmb_genomic.tsv")
tumor_descriptor_color_palette_file <- file.path(root_dir, "figures", "palettes", "tumor_descriptor_color_palette.tsv")

# File path to plots directory
plots_dir <-
  file.path(analysis_dir, "plots")
if (!dir.exists(plots_dir)) {
  dir.create(plots_dir)
}

source(paste0(analysis_dir, "/util/function-create-barplot-v1.R"))
source(paste0(root_dir, "/figures/scripts/theme.R"))

Read in data and process

# Read and process tmb_genomic file
tmb_genomic_all <- readr::read_tsv(tmb_genomic_file, guess_max = 100000, show_col_types = FALSE) 

# Are there any samples with both WGS and WXS? 
tmb_genomic_all %>% 
  unique() %>% 
  arrange(Kids_First_Participant_ID, experimental_strategy)  %>%
  group_by(Kids_First_Participant_ID) %>%
  summarise(experimental_strategy_sum = str_c(experimental_strategy, collapse = ";")) 

# Yes, they are, so let's remove these from downstream analyses.
tmb_genomic <- tmb_genomic_all %>% 
  filter(!experimental_strategy == "WXS")

# Read color palette
tumor_descriptor_color_palette <- readr::read_tsv(tumor_descriptor_color_palette_file, guess_max = 100000, show_col_types = FALSE)

TMB per Kids_First_Participant_ID

We will explore TMB per Kids_First_Participant_ID over time by creating stacked barplots.

# Define parameters for function
ylim <- 360
tmb_df <- tmb_genomic

# Run function
fname <- paste0(plots_dir, "/", "TMB-genomic.pdf")
print(fname)
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic.pdf"
p <- create_stacked_barplot(tmb_df = tmb_df, ylim = ylim)
pdf(file = fname, width = 15, height = 6)
print(p)
dev.off()
quartz_off_screen 
                2 

Note that samples with Low TMB defined as ≤5 mutations/Mb, intermediate TMB defined as >5 and ≤20/Mb, high TMB defined as >20 and ≤50 Mb, and very high TMB defined as >50 mutations/Mb.

We notice that there are samples with high TMB (hyper-mutant samples). Next, we will exclude these samples (threshold >= 20) from downstream analysis. Attention is needed in cases with high number of mutations in only one timepoint as this will lead to un-matched longitudinal samples. We will also remove those so we always have matched longitudinal samples.

# Filter df
tmb_genomic_filter <- tmb_genomic %>%
  filter(!tmb >= 20)  %>%
  unique() %>% 
  arrange(Kids_First_Participant_ID, tumor_descriptor) %>%
  group_by(Kids_First_Participant_ID) %>%
  summarise(tumor_descriptor_sum = str_c(tumor_descriptor, collapse = ";")) %>% 
  filter(!tumor_descriptor_sum %in% c("Diagnosis", "Progressive", "Recurrence")) %>% 
  left_join(tmb_genomic, by = c("Kids_First_Participant_ID", "tumor_descriptor_sum")) %>% 
  mutate(cancer_group_sum = ifelse(short_histology == "HGAT", "High-grade glioma",
                                   ifelse(short_histology == "LGAT", "Low-grade glioma", "Other cancer group")),
         cancer_group_sum = replace_na(cancer_group_sum, "Other"),
         patient_id = paste(short_histology, Kids_First_Participant_ID, sep = "_"),
         patient_bs_id = paste(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, sep = "_")) %>% 
  drop_na(tmb)

# Define parameters for function
ylim <- 12.5
tmb_df <- tmb_genomic_filter

# Run function
fname <- paste0(plots_dir, "/", "TMB-genomic-no-hypermutants.pdf")
print(fname)
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic-no-hypermutants.pdf"
p <- create_stacked_barplot(tmb_df = tmb_df, ylim = ylim)
pdf(file = fname, width = 15, height = 8)
print(p)
dev.off()
quartz_off_screen 
                2 

TMB per patient_id and cancer_group_sum

We will explore TMB per cancer group over time by creating stacked barplots. We will plot based on cancer groups presenting with the highest number of samples (High- and Low-grade gliomas) vesrus any other cancer groups.

cancer_groups <- unique(as.character(tmb_genomic_filter$cancer_group_sum))
print(cancer_groups)
[1] "Other cancer group" "Low-grade glioma"   "High-grade glioma" 
for (i in seq_along(cancer_groups)) {
  print(i)
  tmb_genomic_filter_sub <- tmb_genomic_filter %>%
    filter(cancer_group_sum == cancer_groups [i])
  
  if(i == 1) {
    print (cancer_groups [i])
    # Define parameters for function
    ylim <- 8

  } else if (i == 2) {
    print (cancer_groups [i])
    # Define parameters for function
    ylim <- 4.5
    } else {
    print (cancer_groups [i])
    # Define parameters for function
    ylim <- 12.5
    }

  # Run function
  fname <- paste0(plots_dir, "/", "TMB-genomic", "-", cancer_groups[i], ".pdf")
  print(fname)
  p <- create_stacked_barplot_cancer_group_sum(tmb_df = tmb_genomic_filter_sub, ylim = ylim, ct_id = cancer_groups[i])
  pdf(file = fname, width = 12, height = 8)
  print(p)
  dev.off()
}
[1] 1
[1] "Other cancer group"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic-Other cancer group.pdf"
[1] 2
[1] "Low-grade glioma"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic-Low-grade glioma.pdf"
[1] 3
[1] "High-grade glioma"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic-High-grade glioma.pdf"

Number of mutations per patient_bs_id

Here, we want to explore the number of mutations (mutation_count column) per timepoint and biospecimen sample per patient case by creating barplots.


# Define parameters for function
ylim <- 260
tmb_df = tmb_genomic_filter

# Run function
fname <- paste0(plots_dir, "/", "Total-Mutations-patient_bs_id.pdf")
print(fname)
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Total-Mutations-patient_bs_id.pdf"
p <- create_barplot_sample(tmb_df = tmb_df,
                           ylim = ylim)
pdf(file = fname, width = 25, height = 10)
print(p)
dev.off()
quartz_off_screen 
                2 

sessionInfo()
R version 4.2.3 (2023-03-15)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.4.1

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggthemes_4.2.4  lubridate_1.9.2 forcats_1.0.0   stringr_1.5.0   dplyr_1.1.2     purrr_1.0.1     readr_2.1.4    
 [8] tidyr_1.3.0     tibble_3.2.1    ggplot2_3.4.2   tidyverse_2.0.0

loaded via a namespace (and not attached):
 [1] pillar_1.9.0      compiler_4.2.3    bslib_0.5.0       jquerylib_0.1.4   tools_4.2.3       bit_4.0.5        
 [7] digest_0.6.33     timechange_0.2.0  jsonlite_1.8.7    evaluate_0.21     lifecycle_1.0.3   gtable_0.3.3     
[13] pkgconfig_2.0.3   rlang_1.1.1       cli_3.6.1         rstudioapi_0.15.0 parallel_4.2.3    yaml_2.3.7       
[19] xfun_0.39         fastmap_1.1.1     withr_2.5.0       knitr_1.43        generics_0.1.3    vctrs_0.6.3      
[25] sass_0.4.7        hms_1.1.3         bit64_4.0.5       rprojroot_2.0.3   tidyselect_1.2.0  glue_1.6.2       
[31] R6_2.5.1          fansi_1.0.4       vroom_1.6.3       rmarkdown_2.23    farver_2.1.1      tzdb_0.4.0       
[37] magrittr_2.0.3    scales_1.2.1      htmltools_0.5.5   colorspace_2.1-0  labeling_0.4.2    utf8_1.2.3       
[43] stringi_1.7.12    munsell_0.5.0     cachem_1.0.8      crayon_1.5.2     
LS0tCnRpdGxlOiAiQ3JlYXRlIFRNQiBiYXJwbG90cyBvZiB0dW1vcnMgYWNyb3NzIG11bHRpcGxlIHRpbWVwb2ludHMgb2YgdGhlIFBCVEEgQ29ob3J0IgphdXRob3I6ICJBbnRvbmlhIENocm9uaSA8Y2hyb25pYUBjaG9wLmVkdT4gZm9yIEQzQiIKZGF0ZTogIjIwMjMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKLS0tCgojIyMjIFR1bW9yIGV2b2x1dGlvbiBwcm9qZWN0IAoKIyMjIERhdGEgdXNlZCAKSW4gdGhpcyBub3RlYm9vaywgd2UgYXJlIHVzaW5nIHRoZSBgdG1iX2dlbm9taWMudHN2YCBmaWxlIGdlbmVyYXRlZCBmcm9tIHRoZSBgMDEtcHJlcHJvY2Vzcy1kYXRhLlJtZGAgc2NyaXB0LgoKIyBTZXQgdXAKYGBge3IgbG9hZC1saWJyYXJ5fQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQp9KQpgYGAKCiMgRGlyZWN0b3JpZXMgYW5kIEZpbGUgSW5wdXRzL091dHB1dHMKYGBge3Igc2V0LWRpci1hbmQtZmlsZS1uYW1lc30KIyBEZXRlY3QgdGhlICIuZ2l0IiBmb2xkZXIuIFRoaXMgd2lsbCBiZSBpbiB0aGUgcHJvamVjdCByb290IGRpcmVjdG9yeS4KIyBVc2UgdGhpcyBhcyB0aGUgcm9vdCBkaXJlY3RvcnkgdG8gZW5zdXJlIHByb3BlciBzb3VyY2luZyBvZiBmdW5jdGlvbnMKIyBubyBtYXR0ZXIgd2hlcmUgdGhpcyBpcyBjYWxsZWQgZnJvbS4Kcm9vdF9kaXIgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0IikpCnNjcmF0Y2hfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgInNjcmF0Y2giKQphbmFseXNpc19kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiYW5hbHlzZXMiLCAidG1iLXZhZi1sb25naXR1ZGluYWwiKSAKaW5wdXRfZGlyIDwtIGZpbGUucGF0aChhbmFseXNpc19kaXIsICJpbnB1dCIpCgojIElucHV0IGZpbGVzCnRtYl9nZW5vbWljX2ZpbGUgPC0gZmlsZS5wYXRoKHNjcmF0Y2hfZGlyLCAidG1iX2dlbm9taWMudHN2IikKdHVtb3JfZGVzY3JpcHRvcl9jb2xvcl9wYWxldHRlX2ZpbGUgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiZmlndXJlcyIsICJwYWxldHRlcyIsICJ0dW1vcl9kZXNjcmlwdG9yX2NvbG9yX3BhbGV0dGUudHN2IikKCiMgRmlsZSBwYXRoIHRvIHBsb3RzIGRpcmVjdG9yeQpwbG90c19kaXIgPC0KICBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicGxvdHMiKQppZiAoIWRpci5leGlzdHMocGxvdHNfZGlyKSkgewogIGRpci5jcmVhdGUocGxvdHNfZGlyKQp9Cgpzb3VyY2UocGFzdGUwKGFuYWx5c2lzX2RpciwgIi91dGlsL2Z1bmN0aW9uLWNyZWF0ZS1iYXJwbG90LXYxLlIiKSkKc291cmNlKHBhc3RlMChyb290X2RpciwgIi9maWd1cmVzL3NjcmlwdHMvdGhlbWUuUiIpKQpgYGAKCiMgUmVhZCBpbiBkYXRhIGFuZCBwcm9jZXNzCmBgYHtyIHJlYWRfaW5wdXRfZmlsZXN9CiMgUmVhZCBhbmQgcHJvY2VzcyB0bWJfZ2Vub21pYyBmaWxlCnRtYl9nZW5vbWljX2FsbCA8LSByZWFkcjo6cmVhZF90c3YodG1iX2dlbm9taWNfZmlsZSwgZ3Vlc3NfbWF4ID0gMTAwMDAwLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAKCiMgQXJlIHRoZXJlIGFueSBzYW1wbGVzIHdpdGggYm90aCBXR1MgYW5kIFdYUz8gCnRtYl9nZW5vbWljX2FsbCAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGFycmFuZ2UoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgZXhwZXJpbWVudGFsX3N0cmF0ZWd5KSAgJT4lCiAgZ3JvdXBfYnkoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lCiAgc3VtbWFyaXNlKGV4cGVyaW1lbnRhbF9zdHJhdGVneV9zdW0gPSBzdHJfYyhleHBlcmltZW50YWxfc3RyYXRlZ3ksIGNvbGxhcHNlID0gIjsiKSkgCgojIFllcywgdGhleSBhcmUsIHNvIGxldCdzIHJlbW92ZSB0aGVzZSBmcm9tIGRvd25zdHJlYW0gYW5hbHlzZXMuCnRtYl9nZW5vbWljIDwtIHRtYl9nZW5vbWljX2FsbCAlPiUgCiAgZmlsdGVyKCFleHBlcmltZW50YWxfc3RyYXRlZ3kgPT0gIldYUyIpCgojIFJlYWQgY29sb3IgcGFsZXR0ZQp0dW1vcl9kZXNjcmlwdG9yX2NvbG9yX3BhbGV0dGUgPC0gcmVhZHI6OnJlYWRfdHN2KHR1bW9yX2Rlc2NyaXB0b3JfY29sb3JfcGFsZXR0ZV9maWxlLCBndWVzc19tYXggPSAxMDAwMDAsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmBgYAoKIyBUTUIgcGVyIEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQKV2Ugd2lsbCBleHBsb3JlIFRNQiBwZXIgYEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSURgIG92ZXIgdGltZSBieSBjcmVhdGluZyBzdGFja2VkIGJhcnBsb3RzLgoKYGBge3IgY3JlYXRlLXN0YWNrZWQtYmFycGxvdCwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA2LCBmaWcuZnVsbHdpZHRoID0gVFJVRX0KIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KeWxpbSA8LSAzNjAKdG1iX2RmIDwtIHRtYl9nZW5vbWljCgojIFJ1biBmdW5jdGlvbgpmbmFtZSA8LSBwYXN0ZTAocGxvdHNfZGlyLCAiLyIsICJUTUItZ2Vub21pYy5wZGYiKQpwcmludChmbmFtZSkKcCA8LSBjcmVhdGVfc3RhY2tlZF9iYXJwbG90KHRtYl9kZiA9IHRtYl9kZiwgeWxpbSA9IHlsaW0pCnBkZihmaWxlID0gZm5hbWUsIHdpZHRoID0gMTUsIGhlaWdodCA9IDYpCnByaW50KHApCmRldi5vZmYoKQpgYGAKTm90ZSB0aGF0IHNhbXBsZXMgd2l0aCBMb3cgVE1CIGRlZmluZWQgYXMg4omkNSBtdXRhdGlvbnMvTWIsIGludGVybWVkaWF0ZSBUTUIgZGVmaW5lZCBhcyA+NSBhbmQg4omkMjAvTWIsIGhpZ2ggVE1CIGRlZmluZWQgYXMgPjIwIGFuZCDiiaQ1MCBNYiwgYW5kIHZlcnkgaGlnaCBUTUIgZGVmaW5lZCBhcyA+NTAgbXV0YXRpb25zL01iLgoKV2Ugbm90aWNlIHRoYXQgdGhlcmUgYXJlIHNhbXBsZXMgd2l0aCBoaWdoIFRNQiAoaHlwZXItbXV0YW50IHNhbXBsZXMpLiBOZXh0LCB3ZSB3aWxsIGV4Y2x1ZGUgdGhlc2Ugc2FtcGxlcyAodGhyZXNob2xkID49IDIwKSBmcm9tIGRvd25zdHJlYW0gYW5hbHlzaXMuIEF0dGVudGlvbiBpcyBuZWVkZWQgaW4gY2FzZXMgd2l0aCBoaWdoIG51bWJlciBvZiBtdXRhdGlvbnMgaW4gb25seSBvbmUgdGltZXBvaW50IGFzIHRoaXMgd2lsbCBsZWFkIHRvIHVuLW1hdGNoZWQgbG9uZ2l0dWRpbmFsIHNhbXBsZXMuIFdlIHdpbGwgYWxzbyByZW1vdmUgdGhvc2Ugc28gd2UgYWx3YXlzIGhhdmUgbWF0Y2hlZCBsb25naXR1ZGluYWwgc2FtcGxlcy4KCmBgYHtyIGNyZWF0ZS1zdGFja2VkLWJhcnBsb3QtZmlsdGVyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDgsIGZpZy5mdWxsd2lkdGggPSBUUlVFfQojIEZpbHRlciBkZgp0bWJfZ2Vub21pY19maWx0ZXIgPC0gdG1iX2dlbm9taWMgJT4lCiAgZmlsdGVyKCF0bWIgPj0gMjApICAlPiUKICB1bmlxdWUoKSAlPiUgCiAgYXJyYW5nZShLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCB0dW1vcl9kZXNjcmlwdG9yKSAlPiUKICBncm91cF9ieShLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUKICBzdW1tYXJpc2UodHVtb3JfZGVzY3JpcHRvcl9zdW0gPSBzdHJfYyh0dW1vcl9kZXNjcmlwdG9yLCBjb2xsYXBzZSA9ICI7IikpICU+JSAKICBmaWx0ZXIoIXR1bW9yX2Rlc2NyaXB0b3Jfc3VtICVpbiUgYygiRGlhZ25vc2lzIiwgIlByb2dyZXNzaXZlIiwgIlJlY3VycmVuY2UiKSkgJT4lIAogIGxlZnRfam9pbih0bWJfZ2Vub21pYywgYnkgPSBjKCJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIiwgInR1bW9yX2Rlc2NyaXB0b3Jfc3VtIikpICU+JSAKICBtdXRhdGUoY2FuY2VyX2dyb3VwX3N1bSA9IGlmZWxzZShzaG9ydF9oaXN0b2xvZ3kgPT0gIkhHQVQiLCAiSGlnaC1ncmFkZSBnbGlvbWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzaG9ydF9oaXN0b2xvZ3kgPT0gIkxHQVQiLCAiTG93LWdyYWRlIGdsaW9tYSIsICJPdGhlciBjYW5jZXIgZ3JvdXAiKSksCiAgICAgICAgIGNhbmNlcl9ncm91cF9zdW0gPSByZXBsYWNlX25hKGNhbmNlcl9ncm91cF9zdW0sICJPdGhlciIpLAogICAgICAgICBwYXRpZW50X2lkID0gcGFzdGUoc2hvcnRfaGlzdG9sb2d5LCBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCBzZXAgPSAiXyIpLAogICAgICAgICBwYXRpZW50X2JzX2lkID0gcGFzdGUoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgc2VwID0gIl8iKSkgJT4lIAogIGRyb3BfbmEodG1iKQoKIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KeWxpbSA8LSAxMi41CnRtYl9kZiA8LSB0bWJfZ2Vub21pY19maWx0ZXIKCiMgUnVuIGZ1bmN0aW9uCmZuYW1lIDwtIHBhc3RlMChwbG90c19kaXIsICIvIiwgIlRNQi1nZW5vbWljLW5vLWh5cGVybXV0YW50cy5wZGYiKQpwcmludChmbmFtZSkKcCA8LSBjcmVhdGVfc3RhY2tlZF9iYXJwbG90KHRtYl9kZiA9IHRtYl9kZiwgeWxpbSA9IHlsaW0pCnBkZihmaWxlID0gZm5hbWUsIHdpZHRoID0gMTUsIGhlaWdodCA9IDgpCnByaW50KHApCmRldi5vZmYoKQpgYGAKCiMgVE1CIHBlciBwYXRpZW50X2lkIGFuZCBjYW5jZXJfZ3JvdXBfc3VtCldlIHdpbGwgZXhwbG9yZSBUTUIgcGVyIGNhbmNlciBncm91cCBvdmVyIHRpbWUgYnkgY3JlYXRpbmcgc3RhY2tlZCBiYXJwbG90cy4gV2Ugd2lsbCBwbG90IGJhc2VkIG9uIGNhbmNlciBncm91cHMgcHJlc2VudGluZyB3aXRoIHRoZSBoaWdoZXN0IG51bWJlciBvZiBzYW1wbGVzIChIaWdoLSBhbmQgTG93LWdyYWRlIGdsaW9tYXMpIHZlc3J1cyBhbnkgb3RoZXIgY2FuY2VyIGdyb3Vwcy4KCmBgYHtyIGNyZWF0ZS1zdGFja2VkLWJhcnBsb3QtZmlsdGVyLWNhbmNlci1ncm91cC1zdW0sIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuc2hvdz0naG9sZCd9CmNhbmNlcl9ncm91cHMgPC0gdW5pcXVlKGFzLmNoYXJhY3Rlcih0bWJfZ2Vub21pY19maWx0ZXIkY2FuY2VyX2dyb3VwX3N1bSkpCnByaW50KGNhbmNlcl9ncm91cHMpCgpmb3IgKGkgaW4gc2VxX2Fsb25nKGNhbmNlcl9ncm91cHMpKSB7CiAgcHJpbnQoaSkKICB0bWJfZ2Vub21pY19maWx0ZXJfc3ViIDwtIHRtYl9nZW5vbWljX2ZpbHRlciAlPiUKICAgIGZpbHRlcihjYW5jZXJfZ3JvdXBfc3VtID09IGNhbmNlcl9ncm91cHMgW2ldKQogIAogIGlmKGkgPT0gMSkgewogICAgcHJpbnQgKGNhbmNlcl9ncm91cHMgW2ldKQogICAgIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KICAgIHlsaW0gPC0gOAoKICB9IGVsc2UgaWYgKGkgPT0gMikgewogICAgcHJpbnQgKGNhbmNlcl9ncm91cHMgW2ldKQogICAgIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KICAgIHlsaW0gPC0gNC41CiAgICB9IGVsc2UgewogICAgcHJpbnQgKGNhbmNlcl9ncm91cHMgW2ldKQogICAgIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KICAgIHlsaW0gPC0gMTIuNQogICAgfQoKICAjIFJ1biBmdW5jdGlvbgogIGZuYW1lIDwtIHBhc3RlMChwbG90c19kaXIsICIvIiwgIlRNQi1nZW5vbWljIiwgIi0iLCBjYW5jZXJfZ3JvdXBzW2ldLCAiLnBkZiIpCiAgcHJpbnQoZm5hbWUpCiAgcCA8LSBjcmVhdGVfc3RhY2tlZF9iYXJwbG90X2NhbmNlcl9ncm91cF9zdW0odG1iX2RmID0gdG1iX2dlbm9taWNfZmlsdGVyX3N1YiwgeWxpbSA9IHlsaW0sIGN0X2lkID0gY2FuY2VyX2dyb3Vwc1tpXSkKICBwZGYoZmlsZSA9IGZuYW1lLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4KQogIHByaW50KHApCiAgZGV2Lm9mZigpCn0KYGBgCgojIE51bWJlciBvZiBtdXRhdGlvbnMgcGVyIHBhdGllbnRfYnNfaWQKSGVyZSwgd2Ugd2FudCB0byBleHBsb3JlIHRoZSBudW1iZXIgb2YgbXV0YXRpb25zIChgbXV0YXRpb25fY291bnRgIGNvbHVtbikgcGVyIHRpbWVwb2ludCBhbmQgYmlvc3BlY2ltZW4gc2FtcGxlIHBlciBwYXRpZW50IGNhc2UgYnkgY3JlYXRpbmcgYmFycGxvdHMuCgpgYGB7ciBjcmVhdGUtYmFycGxvdC1zYW1wbGUsIGZpZy53aWR0aCA9IDI1LCBmaWcuaGVpZ2h0ID0gMTAsIGZpZy5mdWxsd2lkdGggPSBUUlVFLCBmaWcuc2hvdz0naG9sZCcsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoICA9ICAiNTAlIn0KCiMgRGVmaW5lIHBhcmFtZXRlcnMgZm9yIGZ1bmN0aW9uCnlsaW0gPC0gMjYwCnRtYl9kZiA9IHRtYl9nZW5vbWljX2ZpbHRlcgoKIyBSdW4gZnVuY3Rpb24KZm5hbWUgPC0gcGFzdGUwKHBsb3RzX2RpciwgIi8iLCAiVG90YWwtTXV0YXRpb25zLXBhdGllbnRfYnNfaWQucGRmIikKcHJpbnQoZm5hbWUpCnAgPC0gY3JlYXRlX2JhcnBsb3Rfc2FtcGxlKHRtYl9kZiA9IHRtYl9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeWxpbSA9IHlsaW0pCnBkZihmaWxlID0gZm5hbWUsIHdpZHRoID0gMjUsIGhlaWdodCA9IDEwKQpwcmludChwKQpkZXYub2ZmKCkKYGBgCgpgYGB7ciBlY2hvPVRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCg==